Skip to content

Support adding users to user groups at invitation time (AddOrganizationMemberUser / AddProjectMemberUser usergroups) - #9858

Merged
nishantmonu51 merged 5 commits into
mainfrom
invite_groups
Sep 15, 2026
Merged

nishantmonu51 merged 5 commits into
mainfrom
invite_groups

Conversation

@nishantmonu51

Copy link
Copy Markdown
Collaborator

Admins can now assign user groups when inviting a user, instead of inviting, waiting for acceptance, and then adding them to groups. Follows the pattern used for invite-time attributes in #9797. The org_invites.usergroup_ids column and its application on signup already existed; this PR exposes it through the APIs, CLI, and UI.

Backend

  • AddOrganizationMemberUser and AddProjectMemberUser accept usergroups (group names). For a pending signup they are stored on the org invite and merged additively on re-invite; for an existing user they are applied immediately in the same transaction (ON CONFLICT DO NOTHING, so it is safe inside the tx).
  • A shared resolveUsergroupsForMembership validates groups (exist, not managed, admin-role guard) and is reused by AddUsergroupMemberUser, so the rules cannot drift.
  • OrganizationInvite exposes usergroups; ListUsergroupMemberUsers includes pending invitees with pending_acceptance; RemoveUsergroupMemberUser handles pending invitees; DeleteUsergroup scrubs the group from pending invites.
  • No migration needed. Tests added in rbac_test.go and postgres_test.go.

CLI

  • rill user add --group is repeatable and is sent with the org or project invite in a single call. The interactive fallback no longer drops --attribute values.
  • rill user list shows groups and attributes on pending invites, and a status column for group members.

Frontend

  • UserGroupsMultiSelect in the Add users and Add guests dialogs.
  • Pending rows show their invite groups; a new ManageUserGroupsDialog is reachable from a "Manage groups" action on every row of the users and guests tables.
  • The Create and Edit group dialogs list pending invitees and can add them via search.

Checklist:

  • Covered by tests
  • Ran it and it works as intended
  • Reviewed the diff before requesting a review
  • Checked for unhandled edge cases
  • Linked the issues it closes
  • Checked if the docs need to be updated. If so, create a separate Linear DOCS issue
  • Intend to cherry-pick into the release branch
  • I'm proud of this work!

Admins can now pick user groups when inviting a user, instead of waiting for
the invite to be accepted and adding the user to groups afterwards.

Backend:
- `AddOrganizationMemberUser` and `AddProjectMemberUser` accept `usergroups`.
  For a pending signup the groups are stored on the org invite (additively
  merged on re-invite) and applied on acceptance; for an existing user they are
  applied immediately in the same transaction.
- A shared resolver validates groups (exist, not managed, admin-role guard) and
  is reused by `AddUsergroupMemberUser`.
- `OrganizationInvite` exposes `usergroups`; `ListUsergroupMemberUsers` lists
  pending invitees with `pending_acceptance`; `RemoveUsergroupMemberUser` works
  on pending invitees; deleting a group scrubs it from pending invites.

CLI:
- `rill user add --group` is repeatable and rides along with the org or project
  invite in one call; the interactive fallback keeps attributes.
- `rill user list` shows groups and attributes on pending invites and a status
  column on group member listings.

Frontend:
- Group multi-select in the Add users and Add guests dialogs.
- Pending rows show their groups; a Manage groups action on every user row.
- The Create and Edit group dialogs list and can add pending invitees.
@nishantmonu51 nishantmonu51 added Type:Feature New feature request Size:L Large change: 500-1,999 lines labels Sep 4, 2026
Backend:
- `AddOrganizationMemberUser` checks membership up front and applies the
  requested groups to an existing member in one transaction, reporting the
  conflict only after the commit; the seat quota is skipped for existing members.
- `DeleteUsergroup` scrubs the group from pending invites in the same
  transaction as the delete.
- Group member counts include pending invitees, matching the member listing.
- `AddUsergroupMemberUser` and `RemoveUsergroupMemberUser` share the group
  resolver and its permission and managed-group checks.

CLI:
- `rill user add --group` treats AlreadyExists as success when the server has
  applied the groups, and rejects attributes on the groups-only path.

Frontend:
- Manage groups dialog snapshots the target user and changes before saving and
  locks the dialog while requests are in flight, so a save can no longer apply
  to a different user.
- Add users dialog treats a 409 with selected groups as a partial success:
  caches are refreshed and the notice says the members were added to the groups.
- Group changes invalidate every variant of the per-user groups query and the
  member lists of the affected groups, so the table dropdown no longer goes stale.
- Pending rows are tagged explicitly instead of relying on invitedBy, and the
  invite coercion is shared between the users and guests pages.
- The group multi-select stops paging after a failed page.

Claude-Session: https://claude.ai/code/session_01UYzyaaSjA9gNrGJXDsCeTs
The paginated group member listing orders by the database collation, which sorts
`_` before `-` on CI while Go's byte order does the opposite. Compare the pages
against a single unpaginated listing instead of asserting byte-order increase.

Claude-Session: https://claude.ai/code/session_01UYzyaaSjA9gNrGJXDsCeTs
…ove action

The manage groups dialog seeded its baseline from a cached result that the group dialogs never invalidated, so a group added elsewhere was missing from the list and re-adding it failed as a duplicate. The dialog now refetches on open and seeds only once the fetch settles, and the create, edit, rename and delete group flows invalidate the per-user group lists.

The members table also no longer renders the actions kebab: the groups cell already offers "Manage groups" and the role cell offers "Remove", so it only duplicated Remove. Guest rows keep it since they have no role cell.

Claude-Session: https://claude.ai/code/session_015u9dydg9BYfiyBii2sDKH7
The groups cell is now the only entry point to "Manage groups" from the users table, but it only rendered its dropdown when the member already had groups, so a member could not be added to their first group from the table. The dropdown now opens whenever the manage handler is set, showing "No groups" as its label and only the manage action.

Claude-Session: https://claude.ai/code/session_015u9dydg9BYfiyBii2sDKH7
@nishantmonu51
nishantmonu51 merged commit a923d51 into main Sep 15, 2026
16 checks passed
@nishantmonu51
nishantmonu51 deleted the invite_groups branch September 15, 2026 16:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Size:L Large change: 500-1,999 lines Type:Feature New feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants